#include <iostream>
#include "SqliteWrapper.h"
#include "IncidentLogDatabase.h"

#ifdef _WIN32
    #include <windows.h>
#else
    #include <uuid/uuid.h>
#endif


#define GUID_LEN (36)
#define FIRST_CLONUM (0)
#define SECOND_CLONUM (1)
#define THIRD_CLONUM (2)
#define FOURTH_CLONUM (3)

IncidentLogDatabase::IncidentLogDatabase() : m_pSqliteWrapper(NULL)
{
    
}


IncidentLogDatabase::~IncidentLogDatabase()
{
    if (m_pSqliteWrapper != NULL)
    {
        delete m_pSqliteWrapper;
        m_pSqliteWrapper = NULL;
    }
}


IncidentLogDatabase* IncidentLogDatabase::GetInstance()
{
    static IncidentLogDatabase incidentDb;
    return &incidentDb;
}


bool IncidentLogDatabase::Initialize(const std::string& strDbPath)
{
    if (m_pSqliteWrapper == NULL)
    {
        m_pSqliteWrapper = new SQLiteWrapper();
        if (m_pSqliteWrapper == NULL)
        {
            return false;
        }
    }
    
    if (!m_pSqliteWrapper->Open(strDbPath.c_str()))
    {
        delete m_pSqliteWrapper;
        m_pSqliteWrapper = NULL;
        return false;
    }

    std::string strSQL = "CREATE TABLE IF NOT EXISTS TABLE_INCLOG(key text primary key not null, url text not null, log text not null, isUpdate boolean not null);";
    if (!m_pSqliteWrapper->DirectStatement(strSQL))
    {
        delete m_pSqliteWrapper;
        m_pSqliteWrapper = NULL;
        return false;
    }

    return true;
}


bool IncidentLogDatabase::AddIncidentLog(const std::string &strUrl, const std::string& strLog)
{
    bool bRet = false;
    std::string strKey = GetGuid();
    std::string strSQL = "INSERT INTO TABLE_INCLOG VALUES(?, ?, ?, ?);";
    SQLiteStatement* pStat = m_pSqliteWrapper->Statement(strSQL);
    if (pStat != NULL)
    {
        if (pStat->Bind(FIRST_CLONUM, strKey))
        {
            if (pStat->Bind(SECOND_CLONUM, strUrl))
            {
                if (pStat->Bind(THIRD_CLONUM, strLog))
                {
                    if (pStat->Bind(FOURTH_CLONUM, 0))
                    {
                        if (pStat->Execute())
                        {
                            bRet = true;
                        } 
                    }
                }
            } 
        }
    }

    if (pStat != NULL)
    {
        delete pStat;
        pStat = NULL;
    }

    return bRet;
}


bool IncidentLogDatabase::GetData(const std::string& strUrl)
{
    std::string strSQL = "SELECT log FROM TABLE_INCLOG WHERE url = '" + strUrl + "';";
    SQLiteStatement* pStat = m_pSqliteWrapper->Statement(strSQL);
    if (pStat != NULL)
    {
        while (pStat->NextRow())
        {
           std::cout << pStat->DataType(0) << " - " << pStat->ValueString(0) << std::endl;
        }

        delete pStat;
        pStat = NULL;
    }
    else
    {
        m_pSqliteWrapper->LastError();
        return false;
    }

    return true;
}


int IncidentLogDatabase::CountByUrl(const std::string& strUrl)
{
    std::string strSQL = "SELECT COUNT(*) FROM TABLE_INCLOG WHERE url = '" + strUrl + "';";
    SQLiteStatement* pStat = m_pSqliteWrapper->Statement(strSQL);
    if (pStat != NULL)
    {
        if (pStat->NextRow())
        {
            std::cout << pStat->DataType(0) << " - " << pStat->ValueInt(0) << std::endl;
        }

        delete pStat;
        pStat = NULL;
    }
    else
    {
        m_pSqliteWrapper->LastError();
        return -1;
    }

    return 0;
}


bool IncidentLogDatabase::GetTopData(const std::string& strUrl, const int nTopNum)
{
    // SELECT url, log FROM TABLE_INCLOG  WHERE url = '/clientlog/procincident' LIMIT 2
    std::string strSQL = "SELECT url, log FROM TABLE_INCLOG  WHERE url = '" + strUrl + "' ";
    strSQL += "LIMIT " + std::to_string(nTopNum) + ";";
    SQLiteStatement* pStat = m_pSqliteWrapper->Statement(strSQL);
    if (pStat != NULL)
    {
        while(pStat->NextRow()) // query result has tow row
        {
            std::cout << pStat->DataType(0) << " - " << pStat->ValueString(0) << std::endl; // first column
            std::cout << pStat->DataType(1) << " - " << pStat->ValueString(1) << std::endl; // second column
         }

        delete pStat;
        pStat = NULL;
    }
    else
    {
        m_pSqliteWrapper->LastError();
        return false;
    }

    return true;
}


bool IncidentLogDatabase::UpdateTopData(const std::string& strUrl, const int nTopNum)
{
    // UPDATE TABLE_INCLOG SET isUpdate = 0 WHERE key IN (SELECT key FROM TABLE_INCLOG  WHERE url = '/clientlog/procincident' LIMIT 2 );
    std::string strSQLUpdate = "UPDATE TABLE_INCLOG SET isUpdate = 1 WHERE key IN (";
    std::string strSQL = "SELECT key FROM TABLE_INCLOG  WHERE url = '" + strUrl + "' ";
    strSQL += "LIMIT " + std::to_string(nTopNum) + " );";
    strSQLUpdate += strSQL;

    bool bRet = false;
    SQLiteStatement* pStat = m_pSqliteWrapper->Statement(strSQLUpdate);
    if (pStat != NULL)
    {
        bRet = pStat->Execute();
        delete pStat;
        pStat = NULL;
    }
    else
    {
        m_pSqliteWrapper->LastError();
    }

    return bRet;
}



bool IncidentLogDatabase::DeleteData(const std::string& strUrl)
{
    bool bRet = false;
    std::string strSQL = "DELETE FROM TABLE_INCLOG WHERE url = '" + strUrl + "';";
    if (m_pSqliteWrapper->DirectStatement(strSQL))
    {
       bRet =true;
    }

    return bRet;
}



bool IncidentLogDatabase::DeleteData(const int flag)
{
    bool bRet = false;
    std::string strSQL = "DELETE FROM TABLE_INCLOG WHERE isUpdate = " + std::to_string(flag) + ";";
    if (m_pSqliteWrapper->DirectStatement(strSQL))
    {
        bRet = true;
    }

    return bRet;
}



bool IncidentLogDatabase::ClearDataBase()
{
    bool bRet = false;
    std::string strSQL = "DELETE FROM TABLE_INCLOG;";
    if (m_pSqliteWrapper->DirectStatement(strSQL))
    {
        bRet = true;
    }

    return bRet;
}

void IncidentLogDatabase::Close()
{


}


std::string IncidentLogDatabase::GetGuid()
{
    std::string strGuid;
#ifdef _WIN32
    char szBuffer[GUID_LEN] = { 0 };
    GUID guid;
    if (CoCreateGuid(&guid) == 0)
    {
        _snprintf_s(szBuffer, sizeof(szBuffer), GUID_LEN,
            "%08X%04X%04x%02X%02X%02X%02X%02X%02X%02X%02X",
            guid.Data1, guid.Data2, guid.Data3,
            guid.Data4[0], guid.Data4[1], guid.Data4[2],
            guid.Data4[3], guid.Data4[4], guid.Data4[5],
            guid.Data4[6], guid.Data4[7]);
            strGuid = szBuffer;
    }
#else
    uuid_t Uuid;
    char szUuidBuf[GUID_LEN] = { 0 };
    uuid_generate(Uuid);
    uuid_unparse(Uuid, szUuidBuf);
    strGuid = szUuidBuf;
#endif
    return strGuid;
}